<?php
declare(strict_types=1);

require_once(dirname(__DIR__, 3) . '/vendor/autoload.php');
require_once(dirname(__DIR__, 3). '/includes/configure.php');

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DriverManager;


class ThemeChecker extends ThemeChecker_parent
{

    private const THEME_DIR =  __DIR__ . '/../../../themes/';
    private const DEFAULT_THEMES_CONFIG_FILES = [
       'Malibu'     => self::THEME_DIR . '/Malibu/settings.json',
       'Honeygrid'  => self::THEME_DIR . '/Honeygrid/settings.json',
    ];

    private const findValueInTheme = [
        'gx-brand-height',
        'gx-brand-height-sticky',
        'gx-product-listing-image-height-mobile',
        'gx-product-info-thumbnail-width',
        'gx-product-info-thumbnail-width-mobile',
    ];

    /** @var array<string,string> $themeConfigs */
    private array $themeConfigs = array();

    /** @var null|array<string,string> $activeThemeConfig  */
    private ?array $activeThemeConfig;


    public function proceed()
    {
        parent::proceed();
        $this->run();
    }


    /**
     * @return void
     * @throws \Doctrine\DBAL\Exception
     */
    public function run(): void
    {
        /** @var array<int,array<string,mixed>> $findResult */
        $findResult = array();
        /** @var Connection $connection */
        $connection = self::getConnection();
        /** @var array<int,array<string,mixed>> $searchResultParams */
        $searchResultParams = array();

        // update the array with theme config files
        $this->themeConfigs = $this->findThemeConfigs();
        $activeThemeName = $this->findActiveThemeName($connection);
        $this->activeThemeConfig = $this->findActiveThemeConfig($this->themeConfigs, $activeThemeName);

        // if not exist create table
        $this->createDbTable($connection);

        foreach ($this->themeConfigs as $themeName => $jsonThemeConfigFile) {
            /** @var string|null $jsonStringResult */
            $jsonStringResult = $this->openFile($jsonThemeConfigFile);

            if (!is_null($jsonStringResult)) {
                /** @var array<int,array<string,mixed>> $themeConfigArray */
                $themeConfigArray = $this->decodeJsonInArray($jsonStringResult);

                foreach (self::findValueInTheme as $findConfigInTheme) {
                    $findResult[$themeName] = $this->find($themeConfigArray, $findConfigInTheme);

                    $resultArray = array();
                    $resultArray['host'] = HTTPS_SERVER;
                    $resultArray['shop_name'] = HTTPS_SERVER;
                    $resultArray['theme'] = $themeName;
                    $resultArray['active'] = ($activeThemeName == $themeName) ? '1' : '0';
                    $resultArray['search_param'] = $findResult[$themeName]['search_param'];
                    $resultArray['search_meta'] = $findResult[$themeName]['search_meta'];
                    $searchResultParams[] = $resultArray;
                }

            } else {

                $sortArray = array_reverse(explode('/', $jsonThemeConfigFile));
                $trimJsonThemeConfigFilePath = $sortArray[2] .'/' . $sortArray[1] .'/' . $sortArray[0];

                $errorMessageArray = array(
                    'error' => 'File not found',
                    'custom_file_path' => $trimJsonThemeConfigFilePath
                );

                // if theme file not found
                $findResult[$themeName] = json_encode($errorMessageArray);
                $resultArray = array();
                $resultArray['host'] = HTTPS_SERVER;
                $resultArray['shop_name'] = HTTPS_SERVER;
                $resultArray['theme'] = $themeName;
                $resultArray['active'] = ($activeThemeName == $themeName) ? '1' : '0';
                $resultArray['search_param'] ='NULL';
                $resultArray['search_meta'] =  $findResult[$themeName];
                $searchResultParams[] = $resultArray;
            } // if

        } // foreach

        //save all search results in db
        foreach ($searchResultParams as $searchResultsItem) {
              $this->saveSearchResult($connection, $searchResultsItem);
        }
    }

    /**
     * @param string $filename
     * @return string|null
     */
    private function openFile(string $filename): ?string
    {
        // check the file exist
        if (!file_exists($filename)) {
            return null;
        }
        if (!is_readable($filename)) {
            return null;
        }

        /** @var string|false $result */
        $result = file_get_contents($filename);

        return $result === false ? null : $result;
    }

    /**
     * @param string $contents
     * @return array<int, array<string,mixed>>
     */
    private function decodeJsonInArray(string $contents): array
    {
        /** @var array<int, array<string,mixed>> $themeContents */
        $themeContents = json_decode($contents, true);

        return $themeContents;
    }

    /**
     * @param array<int,array<string,mixed>> $themeConfig
     * @param string $themeConfigName
     * @return array<string,mixed>
     */
    private function find(array $themeConfig, string $themeConfigName): array
    {
        /** @var array<string,string|array<string,mixed>> $resultArray  */
        $resultArray = array();

        /** @var array<string|mixed> $themeConfigItem */
        foreach ($themeConfig as $themeConfigItem) {
            if ($themeConfigItem['name'] === $themeConfigName) {
                $resultArray['search_param'] = $themeConfigName;
                if (is_string($themeConfigItem['value'])) {
                    $resultArray['search_meta'] = $themeConfigItem['value'];
                }
                if (is_array($themeConfigItem['value'])) {
                    $resultArray['search_meta'] = json_encode($themeConfigItem['value']);
                } else {
                    $resultArray['search_meta'] = $themeConfigItem['value'];
                }
            }
        }

        return $resultArray;
    }


    /**
     * @param Connection $connection
     * @return void
     * @throws \Doctrine\DBAL\Exception
     */
    private function createDbTable(Connection $connection): void
    {

        $sql = "CREATE TABLE IF NOT EXISTS `sys_theme_config` ("
            . " `id` int(10) NOT NULL AUTO_INCREMENT,"
            . "  `host` VARCHAR(100) NOT NULL,"
            . "  `shop_name` VARCHAR(50) NOT NULL,"
            . "  `theme` VARCHAR(50) NOT NULL,"
            . "  `active` BOOLEAN NOT NULL,"
            . "  `search_param` VARCHAR(50) NULL,"
            . "  `search_meta` TEXT NULL,"
            . "  `created_at` DATETIME NOT NULL DEFAULT NOW(),"
            . "  PRIMARY KEY (`id`)"
            . ") ENGINE=InnoDB DEFAULT CHARSET=utf8;";

        $result = $connection->executeQuery($sql);

    }

    /**
     * @return Connection
     * @throws \Doctrine\DBAL\Exception
     */
    private static function getConnection(): Connection
    {
        $connectionParams = [
            'dbname'   => DB_DATABASE,
            'user'     => DB_SERVER_USERNAME,
            'password' => DB_SERVER_PASSWORD,
            'host'     => DB_SERVER,
            'port'     => 3306,
            'charset'  => 'UTF8',
            'driver'   => 'pdo_mysql',
            'driverOptions' => [
                PDO::ATTR_STRINGIFY_FETCHES => true,
            ]
        ];

        return DriverManager::getConnection($connectionParams);
    }

    /**
     * @param Connection $connection
     * @param array<string,mixed> $params
     * @return void
     * @throws \Doctrine\DBAL\Exception
     */
    private function saveSearchResult(Connection $connection, array $params): void
    {
       $sql = "INSERT INTO sys_theme_config ("
           . "host, shop_name, theme, active, search_param, search_meta, created_at)"
           . " VALUES ('"
           . $params['host'] . "', '"
           . $params['shop_name'] . "', '"
           . $params['theme'] . "', "
           . $params['active'] . ", '"
           . $params['search_param'] . "', '"
           . $params['search_meta']
           . "', DEFAULT)";

        $result = $connection->executeQuery($sql);
    }

    /**
     * @param Connection $connection
     * @return string
     * @throws \Doctrine\DBAL\Exception
     */
    private function findActiveThemeName(Connection $connection): string
    {
        $sql = "SELECT c.value
                FROM gx_configurations AS c 
                WHERE c.key = 'configuration/CURRENT_THEME'";

        $result = $connection->executeQuery($sql);
        /** @var string $activeTheme */
        $activeTheme = $result->fetchOne();

        return $activeTheme;
    }

    /**
     * @param array<string,string> $themeConfigs
     * @param string $activeThemeName
     * @return array<string,string>|null
     */
    private function findActiveThemeConfig(array $themeConfigs, string $activeThemeName): ?array
    {
        $activeTheme = null;

        foreach ($themeConfigs as $themeName => $themeConfig)
        {
            if ($themeName === $activeThemeName) {
                $activeTheme[$themeName] = $themeConfig;
            }
        }
        return $activeTheme;
    }


    /**
     * @return array<string,string>
     */
    private function findThemeConfigs(): array
    {
        $configArray = array();
        $themeDirectory = new DirectoryIterator(self::THEME_DIR);

        // find all theme directories and save in array
        foreach($themeDirectory as $directoryItem) {
            if ($directoryItem->isDot())
                continue;
            if ($themeDirectory->isDir()) {
                $configArray[$themeDirectory->getFilename()] = self::THEME_DIR . $directoryItem->getFilename() . '/settings.json';
            }
        }

        return $configArray;
    }
}
